/* ******************************************************************
 * This file is part of the escal project and distributed under the
 * terms of the GNU General Public License Version 3
 * Copyright (C) 2008 Epocher (xijiaosdq@gmail.com)
 * See COPYING file for licensing information.
 *******************************************************************/

#include "escal.h"
#include "task_model.h"
#include <iostream>
#include <QtAlgorithms>
#include <QStringList>
#include <QtDebug>

using namespace Escal;

// ************************** BaseTaskModel ******************************

BaseTaskModel::BaseTaskModel(QObject *parent):QAbstractItemModel(parent) {
	setupModelData();
};

int BaseTaskModel::columnCount(const QModelIndex &parent) const
{
	return columnHeaders.size();
};

int BaseTaskModel::rowCount(const QModelIndex &parent) const{
	if(parent.isValid())
		return 0;
	return taskList.size();
};

Qt::ItemFlags BaseTaskModel::flags(const QModelIndex &index) const{
	if (!index.isValid())
		return 0;
	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
};

QVariant BaseTaskModel::headerData(int column, Qt::Orientation orientation,
	int role) const
{
	if (orientation == Qt::Horizontal && role == Qt::DisplayRole){
		return columnHeaders[column];
	}		
	return QVariant();
};

QModelIndex BaseTaskModel::index(int row, int column,
	const QModelIndex &parent ) const 
{
	if(parent == QModelIndex()){
		return createIndex (row, column);
	}
	else{
		return QModelIndex();
	}
}

QModelIndex BaseTaskModel::parent(const QModelIndex &index) const
{
	return QModelIndex();
}

void BaseTaskModel::deleteTasks(const QModelIndexList &indexes){
	QList<int> taskRows;
	QModelIndex index;
	foreach(index, indexes){
		taskRows.append(index.row());
	}
	qSort(taskRows);
	int i;
	BaseTask *task;
	foreach(i, taskRows){
		taskList[i]->cancel();
	}
	int off=0;
	foreach(i, taskRows){
		int p = i+off;
		beginRemoveRows(QModelIndex(), p, p);
		task=taskList[p];
		disconnect(task, SIGNAL(taskDataChanged(BaseTask *, int )),
			this, SLOT(taskDataChanged(BaseTask *, int )));
		taskList.removeAt(p);
		qDebug() << "remove at " << p;
		endRemoveRows();
		emit taskDeleted(task);
		off--;
	}
};

void BaseTaskModel::deleteDataTasks(const QModelIndexList &indexes){
	QList<int> taskRows;
	QModelIndex index;
	foreach(index, indexes){
		taskRows.append(index.row());
	}
	qSort(taskRows);
	int i;
	BaseTask *task;
	foreach(i, taskRows){
		taskList[i]->cancel();
		taskList[i]->deleteData();
	}
	int off=0;
	foreach(i, taskRows){
		int p = i+off;
		beginRemoveRows(QModelIndex(), p, p);
		task=taskList[p];
		qDebug() << task;
		disconnect(task, SIGNAL(taskDataChanged(BaseTask *, int )),
			this, SLOT(taskDataChanged(BaseTask *, int )));
		taskList.removeAt(p);
		qDebug() << "remove at " << p;
		endRemoveRows();
		emit taskDeleted(task);
		off--;
	}
};

void BaseTaskModel::loadTaskList(){
	QFile taskFile(dataDir->filePath(taskListFileName) );
	if(!taskFile.exists())
		return;
	taskFile.open(QIODevice::ReadOnly);
	QDataStream in(&taskFile);
	qint32 taskDataVersion;
	in >> taskDataVersion;
	if(taskDataVersion!=TaskDataVersion_0_1){
		QMessageBox::critical((QWidget*)QObject::parent(),
			"Data Format Recognized",
			QString("We can not recognized the format of %1")
			.arg(taskListFileName));
		return;
	}
	int taskNum;
	in >> taskNum;
	qDebug() << taskNum;
	TaskFactory factory;
	for(int i=0;i<taskNum;i++){
		QUrl url;
		QString urlStr, fileName, folderName;
		qint16 threadNum;
		qint8 statusCode;
		in >> urlStr >> fileName >> folderName >> threadNum
			>> statusCode;
		url.setUrl(urlStr);
		if(!in.status() == QDataStream::Ok){
			break;
		}
		BaseTask *task=factory.createNewTask(url, folderName, 
			fileName , threadNum, QObject::parent());
		if(task) {
			task->load();
			switch(statusCode){
			case(BaseTask::Stopped):
				break;
			case(BaseTask::Running):
				task->start();
				break;
			case(BaseTask::Paused):
				task->pause();
				break;
			case(BaseTask::Canceling):
			case(BaseTask::Failed):
				task->cancel();
				break;
			}
			addTask(task);
		}
	}
}

void BaseTaskModel::saveTaskList(){
	qDebug() << "BaseTaskModel::saveTaskList()";
	QFile taskFile(dataDir->filePath(taskListFileName));
	taskFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
	QDataStream out(&taskFile);
	int taskNum;
	out << qint32(TaskDataVersion_0_1);
	taskNum = taskList.size();
	out << taskNum;
	BaseTask *task;
	foreach(task, taskList){
		QUrl url = task->getUrl();
		QString urlStr = url.toString();
		QString fileName = task->getFileName();
		QString folderName = task->getFolderName();
		qint16 threadNum = task->getTotalThread();
		qint8 statusCode = task->getStatusCode();
		//qDebug() << "statusCode:" << statusCode;
		out << urlStr<< fileName << folderName << threadNum << statusCode;
	}
	taskFile.close();
}

void BaseTaskModel::addTask(BaseTask *task){
	beginInsertRows(QModelIndex(), taskList.size(), taskList.size());
	taskList.append(task);
	connect(task, SIGNAL(taskDataChanged(BaseTask *, int )),
		this, SLOT(taskDataChanged(BaseTask *, int )));
	endInsertRows ();
}

// ****************************** DoneTaskModel ****************************

DoneTaskModel::DoneTaskModel(QObject *parent) : BaseTaskModel(parent) {
	taskListFileName = "done_task_list.dat";
	columnHeaders << tr("File Name") << tr("Length") << tr("Folder") 
		<< tr("Source");
	
};

QVariant DoneTaskModel::data(const QModelIndex &index, int role) const{
	if (!index.isValid())
		return QVariant();

	if (role != Qt::DisplayRole)
		return QVariant();
	double fileSize;
	QString unit;
	switch(index.column()){
	case DN_FILENAME:
			// File Name
		return (taskList[index.row()])->getFileName();
	case DN_LENGTH:
			// File Length
		fileSize = (taskList[index.row()])->getFileSize();
		if(fileSize < 0) return tr("not exist");
		unit = "B";
		if(fileSize >= 1024){
			fileSize /= 1024;
			unit = "KB";
		}
		if(fileSize >= 1024){
			fileSize /=1024;
			unit = "MB";
		}
		if(fileSize >= 1024){
			fileSize /=1024;
			unit = "GB";
		} 
		return QString("%1 %2").arg(fileSize, 0, 'g', 3).arg(unit);
	case DN_FOLDERNAME:
		return (taskList[index.row()])->getFolderName();
	case DN_SOURCEURL:
		return (taskList[index.row()])->getUrl();
	default:
		return QVariant();
	}
	
};

void DoneTaskModel::startTask(const QModelIndex &index){
	qDebug() << "Warning: DoneTaskModel::startTask(), this function should never be called";
};
void DoneTaskModel::pauseTask(const QModelIndex &index){
	qDebug() << "Warning: DoneTaskModel::pauseTask(), this function should never be called";
};
void DoneTaskModel::stopTask(const QModelIndex &index){
	qDebug() << "Warning: DoneTaskModel::stop(), this function should never be called";
};

void DoneTaskModel::setupModelData(){
	
	
};

void DoneTaskModel::addTask(BaseTask *task){
	beginInsertRows(QModelIndex(), taskList.size(), taskList.size());
	taskList.append(task);
	endInsertRows ();
}



// ****************************** UndoneTaskModel ***************************

UndoneTaskModel::UndoneTaskModel(QObject *parent): BaseTaskModel(parent)
{
	taskListFileName = "undone_task_list.dat";
	columnHeaders << tr("File Name") << tr("Length") << tr("Remain") 
		<< tr("Speed") << tr("Progress") << tr("Resource") 
		<< tr("Status") << tr("Timeleft") ;
	timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), this, SLOT(loopUpdate()));
	timer->start(500);
};

UndoneTaskModel::~UndoneTaskModel(){
		BaseTask *task;
	foreach(task, taskList){
		task->deleteLater();
	}
}

QVariant UndoneTaskModel::data(const QModelIndex &index, int role) const{
	if (!index.isValid())
		return QVariant();

	if (role != Qt::DisplayRole)
		return QVariant();
		double fileSize;
	double finishedSize;
	double remainSize;
	double speed;
	qint32 time;
	QString unit;
	QString result;
	switch(index.column()){
	case UN_FILENAME:
			// File Name
		return (taskList[index.row()])->getFileName();
	case UN_LENGTH:
			// File Length
		fileSize = (taskList[index.row()])->getFileSize();
		if(fileSize<0) return QString();
		unit = "B";
		
		if(fileSize >= 1024){
			fileSize /= 1024;
			unit = "KB";
		}
		if(fileSize >= 1024){
			fileSize /=1024;
			unit = "MB";
		}
				if(fileSize >= 1024){
			fileSize /=1024;
			unit = "GB";
		} 
		return QString("%1 %2").arg(fileSize, 0, 'g', 3).arg(unit);
	case UN_REMAIN:
			// Remain Length
		fileSize = (taskList[index.row()])->getFileSize();
		finishedSize = (taskList[index.row()])->getFinishedSize();
		remainSize = fileSize - finishedSize;
		if(remainSize<=0) return QString();
		unit = "B";
		if(remainSize >= 1024){
			remainSize /= 1024;
			unit = "KB";
		}
		if(remainSize >= 1024){
			remainSize /=1024;
			unit = "MB";
		}
				if(remainSize >= 1024){
			remainSize /=1024;
			unit = "GB";
		}
				return QString("%1 %2").arg(remainSize, 0, 
						'g', 3).arg(unit);
	case UN_SPEED:
			// speed
		speed = taskList[index.row()]->getSpeed();
		if(speed == 0) return QString();
		unit = "B/s";
				if(speed >= 1024){
			speed /= 1024;
			unit = "KB/s";
		}
		if(speed >= 1024){
			speed /=1024;
			unit = "MB/s";
		}
				if(speed >= 1024){
			speed /=1024;
			unit = "GB";
		}
		return QString("%1 %2").arg(speed, 0, 'g', 3).arg(unit);
	case UN_PROGRESS:
			// progress
		return taskList[index.row()]->getProgress();
	case UN_RESOURCE:
			int cur, total;
		cur = taskList[index.row()]->getCurThread();
		total = taskList[index.row()]->getTotalThread();
			return QString("%1/%2").arg(cur).arg(total);
	case UN_STATUS:
			// status
			return taskList[index.row()]->getStatus();
	case UN_TIMELEFT:
			// time left 
			int hh, mm, ss;
		time = taskList[index.row()]->getTimeLeft();
		if(time <= 0) return QVariant();
		hh = time / 3600;
		time %= 3600;
		mm = time / 60;
		time %= 60;
		ss = time;
		if(hh) result += tr("%1h ").arg(hh);
		if(mm) result += tr("%1m ").arg(mm);
		result += tr("%1s ").arg(ss);
		return result;
	default:
		return QVariant();
	}
};


void UndoneTaskModel::startTask(const QModelIndex &index){
	if(index.row() > rowCount() -1 )
		return;
	taskList[index.row()]->start();
	
}

void UndoneTaskModel::pauseTask(const QModelIndex &index){
	if(index.row() > rowCount() -1 )
		return;
	taskList[index.row()]->pause();
}	

void UndoneTaskModel::stopTask(const QModelIndex &index){
	if(index==QModelIndex()){
		BaseTask *task;
		foreach(task, taskList){
			task->stop();
		}
		return;
	}
	else if(index.row() > rowCount() -1 )
		return;
	else {
		taskList[index.row()]->stop();
	}
}

void UndoneTaskModel::increaseTask(const QModelIndex &index){
	if(index.row() > rowCount() -1 )
		return;
	taskList[index.row()]->increase();
	
}

void UndoneTaskModel::cutDownTask(const QModelIndex &index){
	if(index.row() > rowCount() -1 )
		return;
	taskList[index.row()]->cutDown();
}

void UndoneTaskModel::setupModelData()
{
	
}

void UndoneTaskModel::taskDataChanged(BaseTask *task, int column){
	int row = taskList.indexOf(task);
	QModelIndex index = createIndex(row, column);
	emit dataChanged(index, index);
	
	if(column == UN_STATUS && task->getStatus() == "Completed"){
		disconnect(task, SIGNAL(taskDataChanged(BaseTask *, int )),
			this, SLOT(taskDataChanged(BaseTask *, int )));
		int i;
		i = taskList.indexOf(task);
		beginRemoveRows(QModelIndex(), i, i);
		taskList.removeAt(i);
		endRemoveRows();
		emit taskCompleted(task);
		saveTaskList();
	}
	//connect(task, SIGNAL(taskDataChanged(BaseTask *, int )),
	//	this, SLOT(taskDataChanged(BaseTask *, int )));
}

void UndoneTaskViewDelegate::paint(QPainter *painter, 
	const QStyleOptionViewItem &option,
	const QModelIndex &index ) const
{
	if (index.column() != UN_PROGRESS) {
		QStyledItemDelegate::paint(painter, option, index);
		return;
	}

	// Set up a QStyleOptionProgressBar to precisely mimic the
	// environment of a progress bar.
	QStyleOptionProgressBar progressBarOption;
	progressBarOption.state = QStyle::State_Enabled;
	progressBarOption.direction = QApplication::layoutDirection();
	progressBarOption.rect = option.rect;
	progressBarOption.fontMetrics = QApplication::fontMetrics();
	progressBarOption.minimum = 0;
	progressBarOption.maximum = 100;
	progressBarOption.textAlignment = Qt::AlignCenter;
	progressBarOption.textVisible = true;

	// Set the progress and text values of the style option.
	double progress = (index.model())->data(index).toDouble();
	progressBarOption.progress = progress < 0 ? 0 : progress ;
	progressBarOption.text = QString("%1 %").arg(progress, 0, 'f', 1);

	// Draw the progress bar onto the view.
	QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
};

void UndoneTaskModel::loopUpdate(){
	BaseTask *task;
	foreach(task, taskList){
		taskDataChanged(task, UN_SPEED);
		taskDataChanged(task, UN_TIMELEFT);
	}
}
